package com.wxmit.module.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.wxmit.base.enums.BusinessStatus;
import com.wxmit.base.enums.YesOrNoEnum;
import com.wxmit.base.util.DateUtils;
import com.wxmit.base.util.StringUtils;
import com.wxmit.framework.data.permission.core.annotation.DataPermission;
import com.wxmit.framework.excel.domain.ExcelImportResult;
import com.wxmit.framework.excel.handler.ExcelCellSelectFieldHandler;
import com.wxmit.framework.excel.util.ExcelUtils;
import com.wxmit.framework.satoken.core.util.StpUserUtil;
import com.wxmit.module.system.domain.SysDept;
import com.wxmit.module.system.domain.SysUser;
import com.wxmit.module.system.domain.SysUserRole;
import com.wxmit.module.system.domain.dto.UpdateUserPwDTO;
import com.wxmit.module.system.domain.vo.SysUserVO;
import com.wxmit.module.system.listener.ExcelSysUserDeptReadListener;
import com.wxmit.module.system.mapper.SysUserMapper;
import com.wxmit.module.system.service.ISysDeptService;
import com.wxmit.module.system.service.ISysUserService;
import com.wxmit.system.base.core.ShushanPage;
import com.wxmit.system.base.core.ShushanServiceImpl;
import com.wxmit.system.base.domain.PageDTO;
import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.io.InputStream;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description SysUserServiceImpl
 * @Author huangxiong
 * @Date 2022/7/11 14:46
 * @Version V1.0
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ShushanServiceImpl<SysUserMapper, SysUser> implements ISysUserService, ExcelCellSelectFieldHandler {

    @Value("${ss.customer.dfPassword}")
    private String DF_PASSWORD;

    @Value("${ss.customer.banNum.user}")
    private Integer userBanNum;

    @Autowired
    private ISysDeptService sysDeptService;


    @Override
    public SysUser getByUserName(String userName){
        SysUser sysUser = this.baseMapper.getUserByUserName(userName);
        return sysUser;
    }

    @Override
    public SysUserVO getUserInfo(Long userId) {
        return baseMapper.getUserInfo(userId);
    }

    @DataPermission
    @Override
    public ShushanPage listPage(PageDTO<SysUser> pageDTO) {
        ShushanPage<SysUser> page = pageDTO.getPage();
        SysUser searchData = pageDTO.getSearchData();
        LambdaQueryChainWrapper<SysUser> lambdaQuery = lambdaQuery();
        lambdaQuery.likeRight(StringUtils.isNotBlank(searchData.getNickName()),SysUser::getNickName,searchData.getNickName());
        lambdaQuery.apply(Objects.nonNull(searchData.getLoginDate()),"Date(login_date) = {0}", DateUtils.format(searchData.getLoginDate(),DateUtils.YYYY_MM_DD));
        return lambdaQuery.page(page);
    }



    @Override
    public SysUser getInfo(Long id) {
        return baseMapper.getInfo(id);
    }

    @Override
    public Boolean resetPw(Long id) {
        checkAdmin(id);
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setPassword(StpUserUtil.encryptionPw(DF_PASSWORD));
        sysUser.setDfPw(BusinessStatus.SUCCESS.code);
        boolean result = super.updateById(sysUser);
        if(!result)return result;
        StpUserUtil.kickout(id);
        log.info("admin:[{}]->reset password :[{}]",StpUserUtil.getLoginId(),id);
        return result;
    }

    @Override
    public Boolean updateStatus(Long id, String status) {
        checkAdmin(id);
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setStatus(status);
        StpUserUtil.kickout(id);
        return super.updateById(sysUser);
    }

    @Override
    @Async
    public void updateLoginStatus(Long id, String clientIP, Date date) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setLoginIp(clientIP);
        sysUser.setLoginDate(date);
        super.updateById(sysUser);
    }

    @Override
    public void updatePw(UpdateUserPwDTO pw) {
        Long userId = StpUserUtil.getUserId();
        checkAdmin(userId);
        SysUser user = getById(userId);
        Assert.isTrue(StpUserUtil.eqPassword(user.getPassword(),pw.getOldPw()),"原密码错误！");
        String encryptionPw = StpUserUtil.encryptionPw(pw.getPwd());
        Assert.isTrue(!Objects.equals(pw.getPwd(),pw.getOldPw()),"新密码不能与旧密码一致！");
        super.updateById(SysUser.builder().id(userId).dfPw(BusinessStatus.FAIL.code).password(encryptionPw).build());
        StpUserUtil.logout();
    }

    @Override
    public void increaseLoginErr(Long id) {
        SysUser user = getById(id);
        if(userBanNum.compareTo(user.getLoginErrOfDay()+1) <= 0 ){
            baseMapper.increaseLoginErr(id, YesOrNoEnum.YES.code);
        }else{
            baseMapper.increaseLoginErr(id,null);
        }
    }

    @Override
    public void clearUpLoginErr(Long id){
        baseMapper.clearUpLoginErr(id);
    }

    @Override
    public void clearUpAllLoginErr(){
        baseMapper.clearUpAllLoginErr();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUserRole(Long userId,Long[] roleIds){
        Db.remove(new QueryWrapper<SysUserRole>().setEntity(new SysUserRole()).lambda().eq(SysUserRole::getUserId,userId));
        List<SysUserRole> sysUserRoles = Arrays.stream(roleIds).map(it -> new SysUserRole(userId, it)).collect(Collectors.toList());
        Db.saveBatch(sysUserRoles);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SysUser entity) {
        checkUserNameUnion(entity.getUserName(), entity.getId());
        String pw;
        //密码加密
        if(StringUtils.isNotBlank(entity.getPassword())){
            pw = StpUserUtil.encryptionPw(entity.getPassword());
            entity.setDfPw(BusinessStatus.FAIL.code);
        }else{
            pw = StpUserUtil.encryptionPw(DF_PASSWORD);
            entity.setDfPw(BusinessStatus.SUCCESS.code);
        }
        entity.setPassword(pw);

        boolean result = super.save(entity);
        if(!result)return result;

        //处理角色关系
        refreshTableRelation(entity,false);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(SysUser entity) {
        checkAdmin(entity.getId());
        checkUserNameUnion(entity.getUserName(), entity.getId());
        entity.setPassword(null);


       // SysUser old = getById(entity.getId());
        boolean result = super.updateById(entity);
        if(result){
            //处理角色关系
            refreshTableRelation(entity,true);
            try{
                //注销登陆用户
                log.info(" kickout User name {}",entity.getUserName());
                StpUserUtil.kickout(entity.getId());
            }catch (Exception e){
                // 不影响正常操作，只打印异常
                log.error("updateById then kick out user is error",e);
            }
        }
        return result;
    }

    @Override
    public void updateAvatar(Long id,String avatar){
        baseMapper.update(new UpdateWrapper<SysUser>().lambda().eq(SysUser::getId,id).set(SysUser::getAvatar,avatar));
    }

    @Override
    public boolean removeByIds(Collection<?> list) {
        list.forEach(it->checkAdmin((Long) it));
        boolean result = super.removeByIds(list);

        // 注销登陆用户
        if(result){
            log.info("admin:[{}] -> delete User id: {}",StpUserUtil.getLoginId(),list);
            list.forEach(StpUserUtil::kickout);
        }
        return result;
    }

    @Override
    public void importExcel(InputStream inputStream){
        ExcelReaderBuilder excelReaderBuilder = EasyExcel.read().file(inputStream).head(SysUser.class);
        Map<String,Long> deptNameMap = new HashMap<>();
        sysDeptService.list().forEach(it->{
            deptNameMap.put(it.getName(),it.getId());
        });
        excelReaderBuilder.registerReadListener(new ExcelSysUserDeptReadListener(deptNameMap));
        ExcelImportResult<SysUser> excelImportResult = ExcelUtils.importByClass(excelReaderBuilder, false);
        if(excelImportResult.hasException()){
            throw excelImportResult.getException();
        }
        if(excelImportResult.hasResult()){
            List<SysUser> result = excelImportResult.getResult();
            LambdaQueryChainWrapper<SysUser> lambdaQuery = lambdaQuery();
            lambdaQuery.in(SysUser::getUserName,result.stream().map(SysUser::getUserName).collect(Collectors.toList()));
            Long count = lambdaQuery.count();
            Assert.isTrue(count.compareTo(0L) == 0,"系统中含有重复的登录名，无法批量导入用户！");
            saveBatch(result);
        }
    }


    /**
     *  ExcelCellSelectFieldHandler 的 获取 单元格下拉框的方法。
     *  该对象由框架初始化，不由spring 初始化，需要注意 不要在里面直接使用 bean 对象。
     * @return 单元格下拉框内容。
     */
    @Override
    public List<String> getCellSelectDate(String fieldName) {
        if(SysUser.Fields.deptName.equals(fieldName)){
            // 获取部门列表
            ISysDeptService deptService = SpringUtil.getBean(ISysDeptService.class);
            return deptService.list().stream().map(SysDept::getName).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }


    private void checkUserNameUnion(String userName,Long id){
        LambdaQueryChainWrapper<SysUser> lambdaQuery = lambdaQuery();
        lambdaQuery.eq(SysUser::getUserName,userName);
        if(Objects.nonNull(id)){
            lambdaQuery.ne(SysUser::getId,id);
        }
        Long count = lambdaQuery.count();
        Assert.isTrue(count.compareTo(0L) == 0,"当前登录名已被占用！");
    }
    /**
     *  处理（用户、角色）表关系
     * @param entity 实体
     * @param updateFlag 是否更新
     */
    private void refreshTableRelation(SysUser entity,Boolean updateFlag){
        if(!CollectionUtil.isEmpty(entity.getRoleIds())){
            if(updateFlag){
                Db.remove(new QueryWrapper<SysUserRole>().setEntity(new SysUserRole()).lambda().eq(SysUserRole::getUserId,entity.getId()));
            }
            List<SysUserRole> sysUserRoles = entity.getRoleIds().stream().map(it -> new SysUserRole(entity.getId(), it)).collect(Collectors.toList());
            Db.saveBatch(sysUserRoles);
        }
    }

    private void checkAdmin(Long id){
        Assert.isTrue(!Objects.equals(id,1L),"不可操作管理员用户");
    }
}
